// 8位宽字符
#define PCRE2_CODE_UNIT_WIDTH 8

#include<stdio.h>
#include<string.h>
#include<pcre2.h>

// 排序比较函数
int cmp(const void *p, const void *q);
// 将牌中的字母变为大写
void upper_hand(char *in_hand);
// 将牌分为用#分隔的等级和花色两部分，并对两部分分别排序
void sort_hand(char *in_hand);
// 牌型判断
int classify_poker(const char *in_hand);

// 测试/驱动
int main()
{
    int i, ret;
    char in_hand[][12] = {"2s5s4s3s6s", "8cas7cad3h", "6s2d9c4hts",
                          "asqsjsksts", "asqcjdkhts", "1s5sjsksts"
                         };
    char *poker_type[] = {"Straight flush", "Four of a kind", "Full house",
                          "Flush", "Straight", "Three of a kind",
                          "Two pair", "One pair", "High card"};

    for(i = 0; i < sizeof(in_hand) / sizeof(in_hand[0]); i++)
    {
        upper_hand(in_hand[i]);
        sort_hand(in_hand[i]);
        ret = classify_poker(in_hand[i]);
        puts(poker_type[ret]);
    }

    return 0;
}

// 牌型判断
// 要求等级与花色用#分隔且要求等级与花色分别进行了排序
int classify_poker(const char *in_hand)
{
    // 正则处理相关变量
    pcre2_code *re;
    int rc, i;
    int errornumber;
    PCRE2_SIZE erroroffset;
    pcre2_match_data *match_data;
    PCRE2_SPTR pattern;
    PCRE2_SPTR subject;

    // 正则表达式数组
    const char *pat[] = {
      "(2345A|23456|34567|45678|56789|6789T|"
      "789JT|89JQT|9JKQT|AJKQT)#(.)\\2{4}.*", // #0 同花顺
      "(.)\\1{3}.*#.*", // #1 四张
      "((.)\\2{2}(.)\\3{1}#.*|(.)\\4{1}(.)\\5{2}#.*)", // #2 葫芦(三张加一对)
      ".*#(.)\\1{4}.*", // #3 同花
      "(2345A|23456|34567|45678|56789|6789T|"
      "789JT|89JQT|9JKQT|AJKQT)#.*", // #4 顺子
      "(.)\\1{2}.*#.*", // #5 三张
      "(.)\\1{1}.*(.)\\2{1}.*#.*", // #6 两对
      "(.)\\1{1}.*#.*" // #7 一对
    };

    subject = (PCRE2_SPTR)in_hand;

    for(i = 0; i < 8; i++)
    {
        // 正则表达式
        pattern = (PCRE2_SPTR)pat[i];

        // 编译正则表达式
        re = pcre2_compile(pattern,               /* 模式(正则表达式) */
                PCRE2_ZERO_TERMINATED, /* 模式长度('\0'终止字符串) */
                0,                     /* 默认选项 */
                &errornumber,          /* 错误编码 */
                &erroroffset,          /* 错误信息偏移量 */
                NULL);                 /* 上下文，一般取NULL */
        /* 编译失败处理. */
        if(re == NULL)
        {
            PCRE2_UCHAR buffer[256];
            pcre2_get_error_message(errornumber, buffer, sizeof(buffer));
            printf("PCRE2 compilation failed at offset %d: %s\n",
                   (int)erroroffset, buffer);
            exit(EXIT_FAILURE);
        }

        // 构造匹配寄存器以记录匹配结果
        match_data = pcre2_match_data_create_from_pattern(re, NULL);
        // 匹配
        rc = pcre2_match(re, subject, PCRE2_ZERO_TERMINATED,
                         0, 0, match_data, NULL);

        // 释放内存
        pcre2_match_data_free(match_data);
        pcre2_code_free(re);

        if(rc >= 0)
        {
            // 匹配成功，返回类型码
            return i;
        }
    }

    // 未匹配成功，返回其它类型码
    return i;
}

// 将牌用#分为等级和花色两部分，并对两部分进行排序
void sort_hand(char *s)
{
    char face[6] = {0};
    char suit[6] = {0};
    int i = 0;
    char *p = s;

    while(*p)
    {
        if(i % 2 == 0)
        {
            face[i / 2] = *p;
        }
        else
        {
            suit[i / 2] = *p;
        }
        i++;
        p++;
    }

    qsort(face, strlen(face), 1, cmp);
    qsort(suit, strlen(suit), 1, cmp);
    strcpy(s, face);
    strcat(s, "#");
    strcat(s, suit);
}

// 将字符串中的小写字母变换为大写，其它字符不变
void upper_hand(char *s)
{
    while(*s)
    {
        if(*s >= 'a' && *s <= 'z')
        {
            *s ^= 0x20; // 转换为大写
        }
        s++;
    }
}

// 排序比较函数
int cmp(const void *p, const void *q)
{
    char ch_p = *((char*)p);
    char ch_q = *((char*)q);

    return (ch_p > ch_q) - (ch_p < ch_q);
}
